﻿using Inaction;
using Inaction.Core;
using Inaction.Criteria;
using Inaction.Mapping;
using Inaction.Resource.Properties;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;

namespace Inaction
{
    public abstract partial class Entity<T>
    {
        #region GetChild
        protected Dictionary<SqlStatement, ISavable> _childs = new Dictionary<SqlStatement, ISavable>();

        protected virtual void OnCreatedChild<TList>(TList childs) where TList : IEntityList, new()
        {
        }

        protected virtual TList GetChild<TList, TDetail>(CriteriaBase criteria = null)
            where TList : IEntityList, new()
            where TDetail : Entity<TDetail>, new()
        {
            if (ClassInfo.PrimaryKey == null)
            {
                throw new Exception(string.Format(Resources.NoPrimaryKeys, Entity<T>.ClassInfo.TableName));
            }
            var sql = EntitySqlStatement.GetSelectStatement<TDetail>(criteria);
            return GetChild<TList, TDetail>(sql);
        }

        protected virtual TList GetChild<TList, TDetail>(Expression<Func<TDetail, bool>> e)
            where TList : IEntityList, new()
            where TDetail : Entity<TDetail>, new()
        {
            if (ClassInfo.PrimaryKey == null)
            {
                throw new Exception(string.Format(Resources.NoPrimaryKeys, Entity<T>.ClassInfo.TableName));
            }
            var sql = Linq.LinqHelper<TDetail>.CreateSelectSql(e);
            return GetChild<TList, TDetail>(sql);
        }

        private TList GetChild<TList, TDetail>(SqlStatement sql)
            where TList : IEntityList, new()
            where TDetail : Entity<TDetail>, new()
        {
            bool containsFk = false;
            foreach (var fk in Entity<TDetail>.ClassInfo.Table.ForeignKeys)
            {
                if (sql.WhereText.Contains(fk.Column.FormatName))
                {
                    containsFk = true;
                    break;
                }
            }
            if (!containsFk)
            {
                var childClassInfo = ClassInfo.LoadInfo(typeof(TDetail));
                IProperty foreignProperty = null;
                var foreignKey = childClassInfo.Table.ForeignKeys.FirstOrDefault(p => p.ReferenceTable.Name.Equals(ClassInfo.Table.Name, StringComparison.OrdinalIgnoreCase));
                if (foreignKey == null)
                {
                    foreignProperty = ClassInfo.GetChildAddtionRelationship(typeof(TDetail));
                    if (foreignProperty == null)
                    {
                        foreignProperty = childClassInfo.GetMasterAddtionRelationship(typeof(T));
                    }
                    if (foreignProperty == null)
                    {
                        throw new Exception(string.Format(Resources.NoForeignKeyRelationships,
                            Entity<TDetail>.ClassInfo.TableName,
                            Entity<T>.ClassInfo.TableName));
                    }
                }
                else
                {
                    foreignProperty = Entity<TDetail>.ClassInfo.ColumnProperties[foreignKey.Column.Name];
                }
                sql.AddWhereClause(new KeyValuePair<IProperty, object>(foreignProperty, PrimaryKey));
            }

            if (_childs.Keys.Any(p => p.SqlText == sql.SqlText))
            {
                return (TList)_childs.First(p => p.Key.SqlText == sql.SqlText).Value;
            }

            try
            {
                TList t = (TList)Inaction.Proxy.Query(typeof(TList), sql);
                t.Master = this;
                var enumerator = t.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    (enumerator.Current as TDetail).Master = this;
                }
                _childs.Add(sql, t);
                OnCreatedChild(t);
                return t;
            }
            catch (Exception)
            {
                throw;
            }
        }

        #endregion

        #region addtionRelationship
        public static void AddChildRelationship<Master>(Property<Guid?> property) where Master : Entity<Master>, new()
        {
            var classInfo = ClassInfo.LoadInfo(typeof(T));
            classInfo.AddChildRelationship<Master>(property);
        }
        public static void AddMasterRelationship<Child>(Property<Guid?> property) where Child : Entity<Child>, new()
        {
            var classInfo = ClassInfo.LoadInfo(typeof(T));
            classInfo.AddMasterRelationship<Child>(property);
        }
        #endregion
    }
}
